package com.hsgene.order.service.impl;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.hsgene.common.ClientType;
import com.hsgene.common.DeleteFlagType;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.character.CharactersConstant;
import com.hsgene.common.util.qiniu.QiniuCertificateUtil;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.coupon.dto.GeneticDiscountDto;
import com.hsgene.generator.constants.GeneratorEnum;
import com.hsgene.generator.dto.GeneratorDto;
import com.hsgene.order.constants.v1.OrderStatus;
import com.hsgene.order.domain.applets.*;
import com.hsgene.order.domain.billing.dto.OrderInvoiceInfoDto;
import com.hsgene.order.domain.v1.GeneticTestingOrder;
import com.hsgene.order.domain.v1.GeneticTestingOrderPayment;
import com.hsgene.order.exception.OrderErrorCode;
import com.hsgene.order.persistence.AppletsOrderRepository;
import com.hsgene.order.persistence.InvoiceOpenRepository;
import com.hsgene.order.persistence.OrderRepository;
import com.hsgene.order.queue.OrderInfo;
import com.hsgene.order.queue.OrderQueueInfo;
import com.hsgene.order.service.AppletsOrderService;
import com.hsgene.order.service.api.DiscountCouponService;
import com.hsgene.order.service.api.GeneratorService;
import com.hsgene.order.service.api.ProductService;
import com.hsgene.order.utils.OrderUtils;
import com.hsgene.user.domain.AppletsUserInfoDto;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 小程序订单service
 *
 * @author wxf
 * @date 2018/10/16 14:30
 **/
@Service
public class AppletsOrderServiceImpl implements AppletsOrderService {
    /**
     * Log
     */
    private final static Logger LOGGER = LogManager.getLogger(AppletsOrderServiceImpl.class);

    @Resource
    private AppletsOrderRepository appletsOrderRepository;

    @Resource
    private GeneratorService generatorService;

    @Resource
    private OrderRepository orderRepository;

    @Resource
    private ProductService productService;

    @Resource
    private DiscountCouponService discountCouponService;

    @Resource
    private InvoiceOpenRepository invoiceOpenRepository;

    private ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("thread-order-cancer-%d").build();

    private ExecutorService executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new
            LinkedBlockingQueue<>(), factory);
    /**
     * 延迟队列
     */
    private static DelayQueue<OrderQueueInfo> queue = new DelayQueue<>();

    /**
     * 服务启动，任务初始化
     */
    @PostConstruct
    public void initQueue() {
        List<OrderInfo> orderQueueInfos = appletsOrderRepository.getAllNoPayOrder();
        if (orderQueueInfos == null || orderQueueInfos.isEmpty()) {
            //过期未支付订单取消
            executor.execute(new OrderExpireThread(queue));
            return;
        }
        for (OrderInfo info : orderQueueInfos) {
            Date expireDate = info.getStartTime();
            if (expireDate == null) {
                continue;
            }
            OrderQueueInfo queueInfo = new OrderQueueInfo(info.getOrderId(), expireDate.getTime());
            queue.offer(queueInfo);
        }
        //过期未支付订单取消
        executor.execute(new OrderExpireThread(queue));
    }

    /**
     * 提交订单
     *
     * @param orderInfo 提交信息
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    public String submitOrder(AppletsOrderSubmitInfoDto orderInfo) {
        //获取订单64编号和订单id32位
        String orderNo = getOrderNo(GeneratorEnum.GTPF.getType());
        String orderId = getOrderId();
        Date createTime = new Date();
        //数据插入genetic_testing_order表
        appletsOrderRepository.inserOrderBasicInfo(orderId, orderNo, orderInfo, createTime, OrderUtils
                .strListToJsonStr(orderInfo.getTestProves()));
        //生成genetic_testing_order_addtion_a、genetic_testing_order_addtion_b订单
        appletsOrderRepository.inserOrderA(getOrderId(), orderId, createTime);
        appletsOrderRepository.inserOrderB(getOrderId(), orderId, orderInfo.getSumPrice(), orderInfo
                .getDiscountPrice());
        //订单套餐genetic_testing_order_package
        List<AppletsOrderPackageInfoDto> packageInfoDtos = orderInfo.getPackageInfoDtos();
        appletsOrderRepository.insertOrderPackage(orderId, packageInfoDtos);
        //支付信息genetic_testing_order_payment
        appletsOrderRepository.inserPayInfo(getOrderId(), orderId, createTime);
        //套餐被用标记
        ApiResult result = productService.tagPackage(packageInfoDtos.stream().map
                (AppletsOrderPackageInfoDto::getPackageId).collect(Collectors.toList()));
        if (!result.isSucc()) {
            LOGGER.error("订单标记标记商品套餐使用失败!");
            throw new IllegalStateException("提交异常——标记商品套餐使用失败");
        }
        //如果是抬头是不开发票，则不插入发票表
        insertInvoice(orderInfo, orderId);
        //更新套餐历史购买数量
        updatePackageBuyCount(packageInfoDtos);
        //删除购物车中的商品
        appletsOrderRepository.deleteShoppingCarPackage(orderInfo.getUserId(), packageInfoDtos);
        //优惠券使用
        String disCountId = orderInfo.getDiscountId();
        if (StringUtils.isNotEmpty(disCountId)) {
            appletsOrderRepository.updateUserDiscount(orderInfo.getUserId(), disCountId, 3);
        }
        //过期时间
        long expireTime = createTime.getTime() + 2 * 60 * 60 * 1000;
        OrderQueueInfo queueInfo = new OrderQueueInfo(orderId, expireTime);
        queue.offer(queueInfo);
        return orderId;
    }

    private void insertInvoice(AppletsOrderSubmitInfoDto orderInfo, String orderId) {
        String invoiceTitle = orderInfo.getInvoiceTitle();
        if (StringUtils.isEmpty(invoiceTitle)) {
            return;
        }
        if (invoiceTitle.equals("个人") || invoiceTitle.equals("单位")) {
            OrderInvoiceInfoDto invoiceInfoDto = new OrderInvoiceInfoDto();
            invoiceInfoDto.setInvoiceId(orderInfo.getInvoiceId());
            invoiceInfoDto.setInvoiceStatus(0);
            invoiceInfoDto.setInvoiceMoney(orderInfo.getSumPrice());
            invoiceInfoDto.setOrderId(orderId);
            appletsOrderRepository.inserInvoiceInfo(invoiceInfoDto);
        }
    }

    private class OrderExpireThread implements Runnable {

        private DelayQueue<OrderQueueInfo> queue;

        public OrderExpireThread(DelayQueue<OrderQueueInfo> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    OrderQueueInfo orderQueueInfo = queue.take();
                    //订单过期关闭
                    appletsOrderRepository.orderAutomaticCancer(orderQueueInfo.getOrderId());
                    //删除发票信息
                    appletsOrderRepository.deleteOrderInvoice(orderQueueInfo.getOrderId());
                    LOGGER.info("order is expire，automatic close，orderId : " + orderQueueInfo.getOrderId());
                }
            } catch (Exception e) {
                LOGGER.error("update expire order is error...", e);
            }
        }
    }

    @Async
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    protected void updatePackageBuyCount(List<AppletsOrderPackageInfoDto> packageInfoDtos) {
        for (AppletsOrderPackageInfoDto packageInfoDto : packageInfoDtos) {
            orderRepository.updatePackageBuyCount(packageInfoDto.getPackageId(), 1);
        }
    }

    /**
     * 付款更新订单
     *
     * @param paymentInfo 订单id
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    public Boolean payUpdate(GeneticTestingOrderPayment paymentInfo) {
        try {
            Date updateTime = paymentInfo.getUpdateDateTime();
            if (updateTime == null) {
                updateTime = new Date();
            }
            String orderId = paymentInfo.getOrderId();
            //更新genetic_testing_order
            Boolean flag = appletsOrderRepository.updateOrder(orderId, updateTime,
                    OrderStatus.PERFECTED_DISTRIBUTION.getCode(), OrderStatus.ORDER_CHARGED.getValue());
            //更新genetic_testing_order_package
            appletsOrderRepository.updateOrderPackage(orderId, updateTime);
            //更新genetic_testing_order_payment
            appletsOrderRepository.updateOrderPayment(paymentInfo, updateTime);
            //未付款队列移除
            queue.remove(new OrderQueueInfo(paymentInfo.getOrderId(), 0));
            return flag;
        } catch (Exception e) {
            LOGGER.error("pay upate order is error...", e);
            return false;
        }
    }

    /**
     * 校验订单套餐是否失效
     *
     * @param orderId 订单id
     * @return
     */
    @Override
    public List<AppletsOrderPackageInfoDto> checkOrderPackage(String orderId) {
        return appletsOrderRepository.checkOrderPackage(orderId);
    }

    /**
     * 获取订单列表
     *
     * @param userId      用户id
     * @param orderStatus 订单状态
     * @param page        分页
     * @param size
     * @return
     */
    @Override
    public AppletsListInfoDto getOrderList(String userId, String orderStatus, Integer page, Integer size) {
        AppletsListInfoDto listInfoDto = new AppletsListInfoDto();
        String[] orderStatusArray = StringUtils.isEmpty(orderStatus) ? null : orderStatus.split(CharactersConstant
                .COMMA_EN);
        //获取总数
        Integer sumCount = appletsOrderRepository.getOrderSum(userId, orderStatusArray);
        if (sumCount == null) {
            listInfoDto.setSumCount(0);
            listInfoDto.setTotalPage(0);
            return listInfoDto;
        }
        listInfoDto.setSumCount(sumCount);
        listInfoDto.setTotalPage(sumCount % size == 0 ? sumCount / size : sumCount / size + 1);
        //获取对应分类列表总数
        List<AppletsOrderTypeInfo> typeInfos = appletsOrderRepository.getOrderTypeInfo(userId);
        listInfoDto.setTypeInfos(typeInfos);

        List<AppletsOrderListInfoDto> infoDtos = appletsOrderRepository.getOrderList(userId, orderStatusArray, (page
                - 1) *
                size, size);
        //图片七牛获取
        if (infoDtos == null || infoDtos.isEmpty()) {
            return listInfoDto;
        }
        infoDtos.stream().forEach(infoDto -> {
            infoDto.getPackageList().stream().forEach(packageInfo -> {
                String imageUrl = packageInfo.getImageUrl();
                String result = QiniuCertificateUtil.getDownloadCertificateStr(imageUrl);
                packageInfo.setImageUrl(StringUtils.isEmpty(result) ? imageUrl : result);
            });
        });
        listInfoDto.setInfoDtoList(infoDtos);
        return listInfoDto;
    }

    /**
     * 获取列表统计信息
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public List<AppletsOrderTypeInfo> getOrderTypeInfo(String userId) {
        return appletsOrderRepository.getOrderTypeInfo(userId);
    }

    /**
     * 取消订单
     *
     * @param cancerInfoDto 取消信息
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    public Boolean orderCancer(AppletsOrderCancerInfoDto cancerInfoDto) {
        Boolean flag = appletsOrderRepository.orderCancer(cancerInfoDto, new Date());
        //队列移除
        queue.remove(new OrderQueueInfo(cancerInfoDto.getOrderId(), 0));
        return flag;
    }

    /**
     * 校验订单是否有效
     *
     * @param orderId 订单id
     * @param status  状态
     * @return
     */
    @Override
    public Integer checkOrderIsEffect(String orderId, Integer status) {
        return appletsOrderRepository.checkOrderIsEffect(orderId, status);
    }

    /**
     * 订单评论
     *
     * @param commentInfoDto 评论信息
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    public void orderComment(AppletsOrderCommentInfoDto commentInfoDto) {
        //根据用户ID获取用户信息
        AppletsUserInfoDto userInfo = appletsOrderRepository.getUserInfo(commentInfoDto.getUserId());
        Date time = new Date();
        //评价
        appletsOrderRepository.orderComment(userInfo, time, commentInfoDto);
        //评价之后更新订单
        appletsOrderRepository.updateOrder(commentInfoDto.getOrderId(), time, OrderStatus.DONE.getCode(), null);
    }

    /**
     * 校验用户订单
     *
     * @param orderId 订单id
     * @param userId  用户id
     * @param status  状态
     * @return
     */
    @Override
    public Integer checkUserOrder(String orderId, String userId, Integer status) {
        return appletsOrderRepository.checkUserOrder(orderId, userId, status);
    }

    /**
     * 获取订单详情
     *
     * @param orderId 订单id
     * @return
     */
    @Override
    public AppletsOrderDetailsInfoDto getOrderDetails(String orderId) {
        //获取订单详情
        AppletsOrderDetailsInfoDto detailsInfoDto = appletsOrderRepository.getOrderDetails(orderId);
        //获取收件人信息
        AppletsOrderConsigneeInfoDto consigneeInfoDto = appletsOrderRepository.getConsigneeInfo(detailsInfoDto
                .getConsigneeAddressId());
        //获取检测人信息
        AppletsOrderConsigneeInfoDto testInfoDto = appletsOrderRepository.getConsigneeInfo(detailsInfoDto
                .getTesterAddressId());
        //优惠券信息
        ApiResult<List<GeneticDiscountDto>> apiResultDiscount = discountCouponService.queryEffectiveDiscount
                (ClientType.APPLETS.getCode(), null);
        if (apiResultDiscount.isSucc()) {
            List<GeneticDiscountDto> discountDtoList = apiResultDiscount.getData();
            if (discountDtoList != null && !CollectionUtils.isEmpty(discountDtoList)) {
                detailsInfoDto.setDiscountInfoDtos(discountDtoList);
            }
        }
        detailsInfoDto.setConsigneeInfo(consigneeInfoDto);
        detailsInfoDto.setTesterInfo(testInfoDto);
        //获取物流信息
        AppletsOrderLogisticInfoDto logisticInfoDto = appletsOrderRepository.getOrderLogistic(orderId);
        // 图片加签
        detailsInfoDto.setLogisticInfoDto(logisticInfoDto);
        if (!CollectionUtils.isEmpty(detailsInfoDto.getTestProves())) {
            detailsInfoDto.setTestProves(QiniuCertificateUtil.getDownloadCertificateList(detailsInfoDto.getTestProves
                    ()));
        }
        // 图片加签
        if (!CollectionUtils.isEmpty(detailsInfoDto.getPackageInfoDtos())) {
            detailsInfoDto.getPackageInfoDtos().forEach(e -> {
                if (StringUtils.isNotBlank(e.getImageUrl())) {
                    e.setImageUrl(QiniuCertificateUtil.getDownloadCertificateStr(e.getImageUrl()));
                }
            });
        }
        return detailsInfoDto;
    }

    private String getOrderNo(String type) {
        ApiResult<GeneratorDto> generatorResult = generatorService.numberGenerator(type, 0, 0);
        if (generatorResult.isSucc()) {
            return generatorResult.getData().getSerialNumber();
        }
        return getOrderId();
    }

    private String getOrderId() {
        return UUID32.randomUUIDString();
    }


    @Override
    public ApiResult<NullObject> confirmOrderReceipt(String userId, String orderId) {
        GeneticTestingOrder order = orderRepository.findById(orderId);
        // 订单有效性验证
        if (order == null || DeleteFlagType.DELETE.getCode() == order.getDeleteFlag()) {
            return ApiResult.fail(OrderErrorCode.ORDER_NOT_EXISTED);
        }
        if (!userId.equals(order.getCreatorId())) {
            return ApiResult.fail(OrderErrorCode.ORDER_NOT_EXISTED);
        }

        OrderStatus currentStatus = OrderStatus.getEnum(order.getStatus());
        if (!OrderStatus.WAIT_RECEIPT_GOODS.equals(currentStatus)) {
            return ApiResult.fail(OrderErrorCode.ORDER_STATUS_EXCEPTION_FOR_RECEIPT);
        }

        Date now = new Date();
        order.setStatus(OrderStatus.WAIT_COMMENT.getCode());
        order.setReceiveDateTime(now);
        order.setUpdateDateTime(now);

        orderRepository.updateOrderReceipt(order);
        return ApiResult.succ();
    }

}
